﻿namespace LambdaManAi
{
    class DfsTrash : UserFunctions
    {
        public static object MainFunction(params object[] args)
        {
            return Code(() =>
                Pair(Tuple(1, 0, List()), Closure(StepFunction))
            );
        }

        private const int STEPS_IDX = 1;
        private const int WAY_IDX = 2;
        private const int MAX_STEPS = 5;

        public static object StepFunction(params object[] args)
        {
            // arguments
            const int stepState = 0;
            const int worldState = 1;

            Locals locals = new Locals(args);

            return Code(locals, () =>
                (int)GetItem(locals[stepState], 0) == 2
                    ? Pair
                        (
                            Tuple
                            (
                                IsEmptyList(GetTail(GetLastTupleItem(locals[stepState], WAY_IDX))) ||
                                (int)GetItem(locals[stepState], STEPS_IDX) + 1 >= MAX_STEPS
                                    ? 1 : 2,
                                (int)GetItem(locals[stepState], STEPS_IDX) + 1,
                                GetTail(GetLastTupleItem(locals[stepState], WAY_IDX))
                            ),
                            GetItem(GetLastTupleItem(locals[stepState], WAY_IDX), 0)
                        )
                    : StepFunction2(locals[stepState], locals[worldState])
                );
        }

        public static object StepFunction2(params object[] args)
        {
            // arguments
            const int stepState = 0;
            const int worldState = 1;

            // locals (should have numbers just after arguments!)
            const int map = 2;
            const int man = 3;
            const int manY = 4;
            const int manX = 5;
            const int ghosts = 6;
            const int path = 7;
            const int upcell = 8;
            const int rightcell = 9;
            const int downcell = 10;
            const int leftcell = 11;
            const int pathList = 12;
            const int nextPath = 13;

            Locals locals = null;
            locals = new Locals(args)
            {
                {map, () => GetItem(locals[worldState], MapStatus)},
                {man, () => GetItem(locals[worldState], ManStatus)},
                {manY, () => GetItem(GetItem(locals[man], 1), 1)},
                {manX, () => GetItem(GetItem(locals[man], 1), 0)},
                {ghosts, () => GetItem(locals[worldState], GhostStatus)},
                {path, () => BestPathToPill(locals[map], locals[ghosts], locals[manY], locals[manX], 0, -1, GetItem(locals[worldState], FruitSatus))},
                {upcell, () =>  NormalizeCell(GetCell(locals[map], (int)locals[manY]-1, locals[manX]))},
                {rightcell, () => NormalizeCell(GetCell(locals[map], locals[manY], (int)locals[manX]+1))},
                {downcell, () => NormalizeCell(GetCell(locals[map], (int)locals[manY]+1, locals[manX]))},
                {leftcell, () => NormalizeCell(GetCell(locals[map], locals[manY], (int)locals[manX]-1))},
                {pathList, () => GetLastTupleItem(locals[path], 2)},
                {nextPath, () => IsEmptyList(locals[pathList]) ? List() : GetTail(locals[pathList]) },
            };

            return Code(locals, () =>
                Pair(
                    Tuple(IsEmptyList(locals[nextPath]) ? 1 : 2, 0, locals[nextPath]),

                    !IsEmptyList(locals[pathList]) ? GetItem(locals[pathList], 0) :
                    ((int)locals[leftcell] != Wall ? MoveLeft :
                    ((int)locals[downcell] != Wall ? MoveDown :
                    ((int)locals[rightcell] != Wall ? MoveRight : MoveUp)))));
        }

        //Поиск в глубину пилюли, сначала вверх, потом вправо, потом вниз, потом влево
        public static object BestPathToPill(params object[] args)
        {
            const int map = 0;
            const int ghosts = 1;
            const int y = 2;
            const int x = 3;
            const int distance = 4;
            const int prevDirection = 5;
            const int fruitStatus = 6;

            //locals
            const int curcell = 7;
            const int nextDirections = 8;

            Locals locals = null;
            locals = new Locals(args)
            {
                {curcell, () =>NormalizeCell(GetCell(locals[map], locals[y], locals[x]))},
                {nextDirections, () => (int)locals[prevDirection] == MoveUp ? List(MoveUp, MoveRight, MoveLeft)
                                     : (int)locals[prevDirection] == MoveRight ? List(MoveUp, MoveRight, MoveDown)
                                     : (int)locals[prevDirection] == MoveDown ? List(MoveRight, MoveDown, MoveLeft)
                                     : (int)locals[prevDirection] == MoveLeft ? List(MoveUp, MoveDown, MoveLeft)
                                     : List(MoveUp, MoveRight, MoveDown, MoveLeft)},
            };

            return Code(locals, () =>
                    Select((int)locals[distance] > 15 || (int)locals[curcell] == Wall, Tuple(0, 0, List()),
                    AppendPathElem(
                        locals[map],
                        locals[ghosts],
                        locals[y],
                        locals[x],
                        locals[distance],
                        locals[prevDirection],
                        locals[fruitStatus],
                        FoldList
                        (
                            locals[nextDirections],
                            Tuple(0, 0, List()), // sum, max and path
                            BestPath()
                        )
                    )
                ));
        }

        public static object BestPath(params object[] args)
        {
            // args of calling function
            const int map = 0;
            const int ghosts = 1;
            const int y = 2;
            const int x = 3;
            const int distance = 4;
            const int prevDirection = 5;
            const int fruitStatus = 6;

            // args of closure
            const int curBestPath = 0;
            const int direction = 1;
            Locals locals = null;
            locals = new Locals(args)
            {
            };

            return Code(locals, () => Closure(
                BestPathImpl(
                    GetParam(args, curBestPath),
                    BestPathToPill(GetEnv(args, 2, map),
                        GetEnv(args, 2, ghosts),
                        NextY(GetEnv(args, 2, y), GetParam(args, direction)),
                        NextX(GetEnv(args, 2, x), GetParam(args, direction)),
                        (int)GetEnv(args, 2, distance) + 1,
                        GetParam(args, direction),
                        GetEnv(args, 2, fruitStatus))
                    )));
        }

        public static object BestPathImpl(params object[] args)
        {
            const int path1 = 0;
            const int path2 = 1;

            Locals locals = null;
            locals = new Locals(args)
            {
            };
            return Code(locals, () =>
                (int)GetItem(locals[path1], 1) > (int)GetItem(locals[path2], 1)
                ? ChooseFirstPath(locals[path1], locals[path2])
                : ChooseFirstPath(locals[path2], locals[path1])
                );
        }

        public static object ChooseFirstPath(params object[] args)
        {
            const int path1 = 0;
            const int path2 = 1;

            Locals locals = null;
            locals = new Locals(args)
            {
            };

            return Code(locals, () =>
                Tuple((int)GetItem(locals[path1], 0) + (int)GetItem(locals[path2], 0), GetTail(locals[path1]))
                );
        }

        public static object AppendPathElem(params object[] args)
        {
            // params
            const int map = 0;
            const int ghosts = 1;
            const int y = 2;
            const int x = 3;
            const int distance = 4;
            const int prevDirection = 5;
            const int fruitStatus = 6;
            const int path = 7;

            //locals 
            const int posValue = 8;
            const int pathSum = 9;
            const int pathMax = 10;
            const int pathList = 11;

            Locals locals = null;
            locals = new Locals(args)
            {
                {posValue, () => PositionValue(locals[map], locals[ghosts], locals[y], locals[x], locals[distance], locals[fruitStatus])},
                {pathSum, () => GetItem(locals[path], 0)},
                {pathMax, () => GetItem(locals[path], 1)},
                {pathList, () => GetLastTupleItem(locals[path], 2)},
            };

            return Code(locals, () =>
                /*IsEmptyList(locals[pathList]) ? locals[path]
                : 
                 */
                (int)locals[prevDirection] == -1 ? locals[path]
                :
                Tuple(
                    (int)locals[pathSum] + (int)locals[posValue],
                    Max2((int)locals[pathSum], (int)locals[posValue]),
                    Pair(locals[prevDirection], locals[pathList])
                ));
        }

        public static object PositionValue(params object[] args)
        {
            const int map = 0;
            const int ghosts = 1;
            const int y = 2;
            const int x = 3;
            const int distance = 4;
            const int fruitStatus = 5;

            Locals locals = null;
            locals = new Locals(args)
            {
            };

            return Code(locals, () =>
                    10000 *
                    ((int)FoldList(
                        locals[ghosts],
                        0,
                        CompareGhost(locals[y], locals[x])
                    )
                    +
                    (int)CellCost(GetCell(locals[map], locals[y], locals[x]), locals[fruitStatus]))
                /// (distance + 1)
                );
        }


        public static object CompareGhost(params object[] args)
        {
            // calling function params
            const int y = 0;
            const int x = 1;

            // closure params
            const int accumulator = 0;
            const int ghost = 1;

            //Locals locals = new Locals(args);

            return Code(() => Closure(
                Select((int)GetEnv(args, 1, x) == (int)GetItem(GetItem(GetParam(args, ghost), Location), 0)
                       && (int)GetEnv(args, 1, y) == (int)GetItem(GetItem(GetParam(args, ghost), Location), 1),

                    Select((int)GetItem(GetParam(args, ghost), Vitality) == VitalityStandard, -15000,
                        Select((int)GetItem(GetParam(args, ghost), Vitality) == VitalityInvisible, -2000, 5000)
                    ),

                    GetParam(args, accumulator)))
                );
        }

        public static object CellCost(params object[] args)
        {
            const int cell = 0;
            const int fruitStatus = 1;
            Locals locals = new Locals(args);
            return Code(locals, () =>
                    (int)locals[cell] == Wall ? -1
                    : (int)locals[cell] == EmptySpace ? 0
                    : (int)locals[cell] == Pill ? 10
                    : (int)locals[cell] == PowerPill ? 1000
                    : (int)locals[cell] == Fruit && (int)locals[fruitStatus] > 0 ? (int)locals[fruitStatus] * 10
                    : 0);
        }

        //public static object Take5(params object[] args)
        //{
        //    const int path = 0;

        //    const int elem0 = 1;
        //    const int path1 = 2;
        //    const int elem1 = 3;
        //    const int path2 = 4;
        //    const int elem2 = 5;
        //    const int path3 = 6;
        //    const int elem3 = 7;
        //    const int path4 = 8;
        //    const int elem4 = 9;
        //    const int path5 = 10;

        //    Locals locals = null;
        //    locals = new Locals(args)
        //    {
        //        { elem0, () => GetItem(locals[path], 0) },
        //        { path1, () =>  locals[elem0] GetLastTupleItem(locals[path], 1)},
        //        { elem1, () => GetItem(locals[path1], 0) },
        //        { path2, () => GetLastTupleItem(locals[path1], 1)},
        //   };

        //}

    }
}
